home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Utilities / Winter Shell 1.0d2 / Source / Libraries / DialogLib / DialogLib.c next >
Encoding:
Text File  |  1994-01-16  |  21.5 KB  |  773 lines  |  [TEXT/KAHL]

  1. /* Library of functions for doing things to dialogs and to items in
  2.     the dialogs. Makes using dialogs somewhat simpler. There are also
  3.     several macros (defined in the header for this library) which handle
  4.     special cases for the functions in this library.
  5.     
  6.     Revision History:
  7.     
  8.     93/12/24 aih
  9.     - added functions for setting/getting a floating point value to/from a
  10.     text field
  11.     
  12.     93/11/16 aih
  13.     - if in pre-7.0 system and using a movable modal dialog then
  14.     a special WDEF (released by MacDTS) is substituted for WDEF 0.
  15.     - improved dialog creation code
  16.         
  17.     93/11/07 aih
  18.     - removed a lot of unneeded and unused code
  19.     
  20.     93/11/05 aih
  21.     - a text record is created for each edit text field, which simplifies
  22.     event handling
  23.     
  24.     93/10/16 aih
  25.     - added a couple of assertions
  26.     - fixed redraw with SetIText in windows whose kind field was not dialogKind
  27.     
  28.     93/03/19 AIH
  29.     - Removed call to AlertFake when a dialog couldn't be loaded since
  30.     this duplicated certain alerts (such as the out of memory alert)
  31.     - Removed library initialization function
  32.     
  33.     93/03/17 AIH
  34.     - Tab keys always select the current edit field (tabs in dialogs
  35.     with multiple fields are handled elsewhere) 
  36.     - Item's rectangle is erased before calling SetIText, so that text
  37.     is drawn correctly
  38.  
  39.     93/03/16 AIH
  40.     - Changed DlgWithin to return only control and edit text items, since
  41.     these are the only item types which respond to user actions (i.e.,
  42.     mouse clicks and keyboard input)
  43.     
  44.     93/03/06 AIH
  45.     - Menu commands are disable if memory is low
  46.     
  47.     93/03/05 AIH
  48.     - Added WinHide
  49.     
  50.     92/03/02 AIH
  51.     - Changed extra window data into a pointer which is saved in
  52.     the window's refCon field. This simplified a lot of the code.
  53.     Thanks goodness for data hiding! It made modifying this
  54.     library a breeze.
  55.  
  56.     92/02/27 AIH
  57.     - TE cut, copy, paste, and clear functions are used instead
  58.     of the corresponding "Dlg" functions, since the Dialog Manager
  59.     functions didn't seem to use the same scrap as the rest of the
  60.     application and system when copying text.
  61.     
  62.     92/02/20 AIH
  63.     - Dialogs are registered with the event library
  64.     - Simplifying event handling
  65.     
  66.     91/11/15 AIH
  67.     - Removed knowledge of dialog color tables when creating dialogs
  68.     or alerts.
  69.     
  70.     91/11/14 AIH
  71.     - Removed checks for system software version 7.0
  72.     
  73.     91/07/08 AIH
  74.     - The apple menu is only disabled when running as an application
  75.     and a modal dialog is created
  76.     
  77.     91/06/01 AIH
  78.     - After a dialog is created its window rectangles are initialized to its
  79.     default position by calling WinRectSet
  80.     - A color dialog is created if color is supported on the Macintosh,
  81.     and the 'dctb' or 'actb' resources are loaded and installed as described
  82.     in IM-V.
  83.     
  84.     91/05/12 AIH
  85.     - Added state and size fields to dialog record
  86.     
  87.     91/05/10 AIH
  88.     - Can create a dialog using either an 'ALRT' or a 'DLOG' template
  89.     
  90.     91/05/07 AIH
  91.     - When a dialog is created the cursor is only set to an arrow if not
  92.     running as an application; this avoids confusing cursors, especially
  93.     when the dialog is being displayed as an indication of a lengthy
  94.     procedure
  95.     - The DlgType... macros are used instead of testing the type directly;
  96.     this is easier to write, debug, and maintain
  97.     
  98.     91/04/26 AIH
  99.     - Dialog positioning is handled by window library, so code for doing it
  100.     was removed from this file
  101.     
  102.     91/04/24 AIH
  103.     - Added data field to dialog, for use by the application
  104.     
  105.     91/04/19 AIH
  106.     - The Apple menu is disabled when a modal dialog is created so that
  107.     system 7.0 will know that we handle menu disabling
  108.     
  109.     91/04/17 AIH
  110.     - The current port is *not* set after a dialog is created
  111.     - Added functions for getting and setting the dialog's default item
  112.     - Added functions for accessing the current edit text field
  113.     
  114.     91/04/09 AIH
  115.     - The dialog is hidden just before it is disposed of to avoid unnecessary
  116.     drawing
  117.     - An alert is displayed if the dialog couldn't be loaded
  118.     - Improved error detection and paid particular attention to purgeable
  119.     resources
  120.     
  121.     91/04/07 AIH
  122.     - Added ability to preload a set of dialogs
  123.     
  124.     91/04/03 AIH
  125.     - Added a few fields to the dialog structure
  126.     
  127.     91/03/31 AIH
  128.     - Eliminated need for menus mask
  129.     
  130.     91/03/24-25 AIH
  131.     - Adapted for international canceling
  132.     - Extended keyboard function keys are now recognized for dialog editing
  133.     - Split modal dialog functions into a separate file
  134.     - Dialogs are allocated by calling NewPtr
  135.     
  136.     91/03/23 AIH
  137.     - When positioning a window, its default size should be used if
  138.     it doesn't entirely fit on the default screen. This requires scanning the
  139.     GDevice list and I don't have any way to debug this without a newer
  140.     Macintosh and multiple monitors.
  141.     - Adapted for new way of quitting the event loop
  142.         
  143.     91/03/20 AIH
  144.     - Instead of redrawing the default control when a window receives an
  145.     activate event, we invalidate its region, and let the update event
  146.     handle the drawing. This should avoid an annoying flicker when the
  147.     control is drawn twice when the window is activated and then receives
  148.     an update event.
  149.     
  150.     91/03/19 AIH
  151.     - Modified initialization function to take a mask indicating which
  152.     menus can be disabled when a modal dialog is displayed
  153.     
  154.     91/03/17 AIH
  155.     - Removed "Get" sub-word from title of most dialog functions
  156.     - Event library is only used to run modal dialogs if compiling as an
  157.     application
  158.     
  159.     91/03/14 AIH
  160.     - Added function to query modal dialogs for last item hit
  161.     
  162.     91/03/12 AIH
  163.     - If the procID of the dialog is 5, and if running in a pre-7.0 system, then
  164.     the procID is changed to use the movable modal WDEF distributed by MacDTS.
  165.     
  166.     91/03/10-11 AIH
  167.     - Adapted modal dialog functions to use my event library, so that
  168.     there will only be one event loop in the entire application
  169.     - Added application settable flag indicating whether this library can
  170.     affect the menu bar when running a modal dialog
  171.     - Added function to return the number of items in a dialog
  172.     
  173.     91/03/02-07 AIH
  174.     - The standard dialog filter function sets the current graf port
  175.     to the dialog's graf port. This is needed by DialogSelect.
  176.     - Added function to draw the outline of the default button in gray
  177.     - The standard dialog filter will not interpret command keys
  178.     (cut, copy, paste) if we're running system 7.0
  179.     - My replacement for ModalDialog will call WNE with a sleep interval
  180.     of 0 if the dialog received an event the last time WNE was called. This
  181.     will allow the filter procedure to get at least a null event immediately
  182.     after it got the previous event, in case it needs to update the dialog
  183.     depending on what the user last did. For instance, if the ok button
  184.     can only be activated if there's text in a field, then the filter
  185.     procedure needs to activate the button *after* DialogSelect has
  186.     been called by my replacement to ModalDialog.
  187.     - Extra parameters to ParamText are passed as NULL pointers instead
  188.     of empty strings (IM-1, p421 says it's ok to do this)
  189.     - The default button is drawn on activate events. This takes advantage of my
  190.     default button CDEF
  191.     
  192.     91/02/27-28 AIH
  193.     - Added function to frame a user item
  194.     - Cursor is set to arrow when a dialog is created
  195.     
  196.     91/02/05 AIH
  197.     - system windows are recognized as valid dialogs by DlgValid
  198.     - DlgModalRun calls EventNext and DlgModalEvent instead of
  199.     ModalDialog. This will give me more control over modal dialogs
  200.     and will give background applications more time.
  201.     
  202.     91/02/03 AIH
  203.     - Removed DlgModalRunOnce since I can't figure out a good way to write
  204.     it. Made DlgModalRun use ModalDialog, since there's no advantage to using
  205.     my functions and since I know ModalDialog works. Someday maybe I'll replace
  206.     ModalDialog, but not today.
  207.     
  208.     91/01/31 AIH
  209.     - added functions to replace the standard ModalDialog
  210.     
  211.     91/01/21 AIH
  212.     - added brief comment describing this file
  213.     - simplified call to DlgParamText
  214.     - added use of DlgErrSetup
  215.     
  216.     91/01/19 AIH
  217.     - added support for escape key
  218.     
  219.     91/01/05 Ari Halberstadt (AIH)
  220.     - inserted this standard header in all files */
  221.  
  222. #include <string.h>
  223. #include <stdlib.h>
  224. #include <TextEdit.h>
  225. #include "pstr.h"
  226. #include "ControlLib.h"
  227. #include "DialogLib.h"
  228. #include "DialogModalLib.h"
  229. #include "DrawLib.h"
  230. #include "EventLib.h"
  231. #include "FloatLib.h"
  232. #include "GlobalLib.h"
  233. #include "KeyLib.h"
  234. #include "LowMemLib.h"
  235. #include "MacLib.h"
  236. #include "MemoryLib.h"
  237. #include "RectangleLib.h"
  238. #include "ResourceConstantsLib.h"
  239. #include "ResourceLib.h"
  240. #include "StringLib.h"
  241. #include "TextLib.h"
  242. #include "WindowLib.h"
  243. #include "MenuLib.h"
  244.  
  245. /* List of preloaded dialogs. This list is searched whenever Dlg    is called. If the dialog's ID is in the list, and if the dialog
  246.     isn't already open, then a pointer to the preloaded dialog is
  247.     returned. Otherwise, the dialog is created from scratch. This feature
  248.     is primarily useful for error alerts, which may be displayed in response
  249.     to such things as out-of-memory and resource manager errors. In such
  250.     cases, it would be impractical to display an error message which requires
  251.     a significant amount of memory or which needs resources. */
  252. static struct {
  253.     Boolean        open;    /* true if dialog is already open */
  254.     DialogPtr    dlg;    /* pointer to dialog */
  255.     ResType        type;    /* type of dialog ('DLOG' or 'ALRT') */
  256.     short            id;    /* id of dialog */
  257. } gPreload[DLG_PRELOAD_MAX];
  258.  
  259. /* true if dialog is valid */
  260. Boolean DlgValid(DialogPtr dlg)
  261. {
  262.     return(dlg && WinIsDialog(dlg));
  263. }
  264.  
  265. /* return number of items in the dialog */
  266. short DlgNItems(DialogPtr dlg)
  267. {
  268.     require(DlgValid(dlg));
  269.     return((*(short *) *((DialogPeek)dlg)->items) + 1);
  270. }
  271.  
  272. /* true if a valid item in the dialog */
  273. Boolean DlgItemValid(DialogPtr dlg, short item)
  274. {
  275.     return(0 < item && item <= DlgNItems(dlg));
  276. }
  277.  
  278. /*----------------------------------------------------------------------------*/
  279. /* getting dialog items */
  280. /*----------------------------------------------------------------------------*/
  281.  
  282. /* return the type of the item */
  283. short DlgType(DialogPtr dlg, short item)
  284. {
  285.     short type;
  286.     Handle hndl;
  287.     Rect box;
  288.     
  289.     require(DlgValid(dlg));
  290.     require(DlgItemValid(dlg, item));
  291.     GetDItem(dlg, item, &type, &hndl, &box);
  292.     return(type);
  293. }
  294.  
  295. /* return the handle to the item */
  296. Handle DlgHandle(DialogPtr dlg, short item)
  297. {
  298.     short type;
  299.     Handle hndl;
  300.     Rect box;
  301.  
  302.     require(DlgValid(dlg));
  303.     require(DlgItemValid(dlg, item));
  304.     GetDItem(dlg, item, &type, &hndl, &box);
  305.     return(hndl);
  306. }
  307.  
  308. /* get the item's box */
  309. void DlgBox(DialogPtr dlg, short item, Rect *box)
  310. {
  311.     short type;
  312.     Handle hndl;
  313.     
  314.     require(DlgValid(dlg));
  315.     require(DlgItemValid(dlg, item));
  316.     GetDItem(dlg, item, &type, &hndl, box);
  317. }
  318.  
  319. /*----------------------------------------------------------------------------*/
  320. /* default item functions */
  321. /*----------------------------------------------------------------------------*/
  322.  
  323. /* return the default item */
  324. short DlgDefault(DialogPtr dlg)
  325. {
  326.     require(DlgValid(dlg));
  327.     return(((DialogPeek)dlg)->aDefItem);
  328. }
  329.  
  330. /* set the default item */
  331. void DlgDefaultSet(DialogPtr dlg, short item)
  332. {
  333.     require(DlgValid(dlg));
  334.     require(! item || DlgItemValid(dlg, item));
  335.  
  336.     /* erase and invalidate the old frame */
  337.     if (DlgItemValid(dlg, DlgDefault(dlg)))
  338.         if (DlgTypeCtl(DlgType(dlg, DlgDefault(dlg))))
  339.             CtlDefaultErase(DlgCtl(dlg, DlgDefault(dlg)));
  340.     
  341.     /* set the new default item */
  342.     ((DialogPeek)dlg)->aDefItem = item;
  343.     
  344.     /* erase and invalidate the new frame */
  345.     if (DlgItemValid(dlg, DlgDefault(dlg)))
  346.         if (DlgTypeCtl(DlgType(dlg, DlgDefault(dlg))))
  347.             CtlDefaultErase(DlgCtl(dlg, DlgDefault(dlg)));
  348. }
  349.  
  350. /* frame the default button */
  351. void DlgDefaultFrame(DialogPtr dlg, Pattern pat)
  352. {
  353.     if (DlgItemValid(dlg, DlgDefault(dlg)))
  354.         if (DlgTypeCtl(DlgType(dlg, DlgDefault(dlg))))
  355.             CtlDefaultFrame(DlgCtl(dlg, DlgDefault(dlg)), pat);
  356. }
  357.  
  358. /*----------------------------------------------------------------------------*/
  359. /* static and edit text item functions */
  360. /*----------------------------------------------------------------------------*/
  361.  
  362. static TextHandle DlgTextHandle(DialogPtr dlg, short item)
  363. {
  364.     TextHandle text = NULL;
  365.     
  366.     require(DlgTypeText(DlgType(dlg, item)));
  367.     if (WinHasExtra(dlg))
  368.         text = WinRegisteredID(dlg, item);
  369.     return(text);
  370. }
  371.  
  372. void DlgTextSelect(DialogPtr dlg, short item, short start, short end)
  373. {
  374.     TextHandle text = DlgTextHandle(dlg, item);
  375.     if (text) {
  376.         if (end > TxLength(text))
  377.             end = TxLength(text);
  378.         TxSelect(text, start, end);
  379.         if (FocusWindow() == dlg)
  380.             FocusSet(text);
  381.         else
  382.             WinFocusSet(dlg, text);
  383.     }
  384.     else
  385.         SelIText(dlg, item, start, end);
  386. }
  387.  
  388. void DlgTextSelectAll(DialogPtr dlg, short item)
  389. {
  390.     DlgTextSelect(dlg, item, 0, SHRT_MAX);
  391. }
  392.  
  393. /* set the text of the edit or static text item */
  394. void DlgTextSet(DialogPtr dlg, short item, const CStr255 str)
  395. {
  396.     CStr255 tmpstr;
  397.     TextHandle text = NULL;
  398.     
  399.     require(DlgTypeText(DlgType(dlg, item)));
  400.     require(StrValid(str, sizeof(CStr255)));
  401.     DlgText(dlg, item, tmpstr);
  402.     if (strcmp(str, tmpstr) != 0) {
  403.         text = DlgTextHandle(dlg, item);
  404.         if (text) {
  405.             TxSetText(text, str, strlen(str));
  406.             TxUpdate(text);
  407.         }
  408.         else {
  409.             short kind = WinKind(dlg);
  410.             WinKindSet(dlg, dialogKind);
  411.             SetIText(DlgHandle(dlg, item), c2pstrcpy((StringPtr) tmpstr, str));
  412.             WinKindSet(dlg, kind);
  413.         }
  414.     }
  415. }
  416.  
  417. /* return the text of the edit or static text item */
  418. void DlgText(DialogPtr dlg, short item, CStr255 str)
  419. {
  420.     TextHandle text = NULL;
  421.  
  422.     require(DlgValid(dlg));
  423.     require(DlgTypeText(DlgType(dlg, item)));
  424.     text = DlgTextHandle(dlg, item);
  425.     if (text)
  426.         str[TxGetText(text, str, sizeof(CStr255)-1)] = 0;
  427.     else {
  428.         GetIText(DlgHandle(dlg, item), (StringPtr) str);
  429.         p2cstr((StringPtr) str);
  430.     }
  431. }
  432.  
  433. /* get the first 'len' characters of the text item */
  434. void DlgTextLen(DialogPtr dlg, short item, char *str, short len)
  435. {
  436.     CStr255 tmpstr;
  437.     
  438.     require(len > 0);
  439.     DlgText(dlg, item, tmpstr);
  440.     strncpy(str, tmpstr, len-1);
  441.     str[len-1] = 0;
  442. }
  443.  
  444. /* set the edit or static text item to the number */
  445. void DlgNumSet(DialogPtr dlg, short item, long num)
  446. {
  447.     Str255 str;
  448.  
  449.     NumToString(num, str);
  450.     DlgTextSet(dlg, item, p2cstr(str));
  451. }
  452.  
  453. /* return the numeric value of the edit or static text item */
  454. long DlgNum(DialogPtr dlg, short item)
  455. {
  456.     CStr255 str;
  457.     long result;
  458.     
  459.     DlgText(dlg, item, str);
  460.     StringToNum(c2pstr(str), &result);
  461.     return(result);
  462. }
  463.  
  464. /* set the edit or static text item to the floating point number */
  465. void DlgFloatSet(DialogPtr dlg, short item, float num, short precision)
  466. {
  467.     CStr31 str;
  468.  
  469.     FloatToString(num, str, precision);
  470.     DlgTextSet(dlg, item, str);
  471. }
  472.  
  473. /* return the floating point value of the edit or static text item */
  474. float DlgFloat(DialogPtr dlg, short item)
  475. {
  476.     CStr31 str;
  477.  
  478.     DlgTextLen(dlg, item, str, sizeof(CStr31));
  479.     return(FloatFromString(str));
  480. }
  481.  
  482. /*----------------------------------------------------------------------------*/
  483. /* clicked item */
  484. /*----------------------------------------------------------------------------*/
  485.  
  486. void DlgClick(DialogPtr dlg, short item)
  487. {
  488.     WinExtraPtr(dlg)->item = item;
  489. }
  490.  
  491. short DlgClicked(DialogPtr dlg)
  492. {
  493.     return(WinExtraPtr(dlg)->item);
  494. }
  495.  
  496. /*----------------------------------------------------------------------------*/
  497. /* positioning dialogs */
  498. /*----------------------------------------------------------------------------*/
  499.  
  500. /* Position dialog on screen at the default location (centered horizontally, one
  501.     third from the top vertically). */
  502. void DlgPosition(DialogPtr dlg)
  503. {
  504.     WinPosition(dlg, 2, 3);
  505. }
  506.  
  507. /* center the dialog */
  508. void DlgCenter(DialogPtr dlg)
  509. {
  510.     WinPosition(dlg, 2, 2);
  511. }
  512.  
  513. /* Place in 'pt' the coordinates for the top left corner of the
  514.     dialog so that the dialog would be centered horizontally and one third
  515.     from the top of the screen. Normally, you'd use DlgBegin() and DlgPosition(),
  516.     and then simply call WinShow() for the dialog. Occassionally, though,
  517.     you'll need to position a dialog whose template specifies
  518.     that it is visible, or which is created and run by the ToolBox,
  519.     such as a Standard File dialog: use this function for such cases. */
  520. void DlgPositionPoint(short id, Point *pt)
  521. {
  522.     Rect bounds = (**(DialogTHndl) ResGet('DLOG', id)).boundsRect;
  523.     RectPositionInScreen(&bounds, 2, 3, pt);
  524. }
  525.  
  526. /*----------------------------------------------------------------------------*/
  527. /* getting and disposing of dialogs */
  528. /*----------------------------------------------------------------------------*/
  529.  
  530. /* uninitialize the dialog */
  531. static void DlgUninitialize(DialogPtr dlg)
  532. {
  533.     short i;
  534.  
  535.     if (dlg) {
  536.         for (i = 1; i <= DlgNItems(dlg); i++) {
  537.             EventObjectType object = WinRegisteredID(dlg, i);
  538.             if (object) {
  539.                 if (DlgTypeText(DlgType(dlg, i)))
  540.                     TxEnd(object);
  541.                 else if (DlgTypeCtl(DlgType(dlg, i)))
  542.                     CtlUninitialize(object);
  543.             }
  544.         }
  545.         if (WinIsModal(dlg))
  546.             WinUnregister(dlg, dlg);
  547.         WinUninitialize(dlg);
  548.     }
  549. }
  550.  
  551. /* initialize the dialog */
  552. static void DlgInitialize(DialogPtr dlg)
  553. {
  554.     short i;
  555.     Boolean first;
  556.     
  557.     TRY {
  558.         if (WinIsModal(dlg)) {
  559.             DlgModalInit(dlg);
  560.             WinRegister(dlg, dlg, DlgModalEventTable());
  561.         }
  562.         first = true;
  563.         for (i = 1; i <= DlgNItems(dlg); i++) {
  564.             if (DlgTypeEditText(DlgType(dlg, i))) {
  565.                 TextHandle text = NULL;
  566.                 Rect bounds;
  567.                 Str255 str;
  568.                 DlgBox(dlg, i, &bounds);
  569.                 InsetRect(&bounds, -(TX_MARGIN + FRAME_WIDTH),
  570.                                          -(TX_MARGIN + FRAME_WIDTH));
  571.                 text = TxBeginEdit(dlg, &bounds);
  572.                 WinRegisterID(dlg, text, i);
  573.                 GetIText(DlgHandle(dlg, i), str);
  574.                 TxSetText(text, (char *) str + 1, *str);
  575.                 TxAutoViewSet(text, true);
  576.                 HideDItem(dlg, i);
  577.                 if (first) {
  578.                     DlgTextSelectAll(dlg, i);
  579.                     first = false;
  580.                 }
  581.             }
  582.             else if (DlgTypeCtl(DlgType(dlg, i))) {
  583.                 CtlInitialize(DlgCtl(dlg, i));
  584.                 WinRegisterID(dlg, DlgCtl(dlg, i), i);
  585.             }
  586.         }
  587.     } CATCH {
  588.         DlgUninitialize(dlg);
  589.     } ENDTRY;
  590. }
  591.  
  592. /* create an invisible dialog from the template */
  593. static DialogPtr DlgBeginTemplate(DialogTemplate *tmpl)
  594. {
  595.     volatile DialogPtr dlg = NULL;
  596.     volatile Handle ditl = NULL;
  597.     WindowLayerType layer = 0;
  598.     WindowPtr behind = NULL;
  599.     
  600.     require(RectValid(&tmpl->boundsRect));
  601.     TRY {
  602.         behind = WinAdjustTemplate((WindowTemplate *) tmpl, &layer);
  603.         ditl = HandleCopy(ResGet('DITL', tmpl->itemsID)); /* copy for NewDialog */
  604.         MemCheck(sizeof(DialogRecord));
  605.         if (MacHasColor()) {
  606.             dlg = NewCDialog(NULL, &tmpl->boundsRect, tmpl->title, false,
  607.                         tmpl->procID, behind, tmpl->goAwayFlag, tmpl->refCon, ditl);
  608.         }
  609.         else {
  610.             dlg = NewDialog(NULL, &tmpl->boundsRect, tmpl->title, false,
  611.                         tmpl->procID, behind, tmpl->goAwayFlag, tmpl->refCon, ditl);
  612.         }
  613.         FailNIL(dlg);
  614.         WinInitialize(dlg, layer);
  615.         DlgInitialize(dlg);
  616.         DlgPosition(dlg);
  617.     } CATCH {
  618.         if (dlg)
  619.             DisposeDialog(dlg);
  620.         HandleEnd(ditl);
  621.     } ENDTRY;
  622.     ensure(DlgValid(dlg) && ! WinVisible(dlg));
  623.     return(dlg);
  624. }
  625.  
  626. /* get a new alert */
  627. static DialogPtr DlgOpenALRT(short id)
  628. {
  629.     AlertTemplate **alrt = NULL;
  630.     DialogTemplate tmpl;
  631.  
  632.     alrt = (AlertTemplate **) ResGet('ALRT', id);
  633.     tmpl.boundsRect = (**alrt).boundsRect;
  634.     tmpl.procID = dBoxProc;
  635.     tmpl.visible = false;
  636.     tmpl.filler1 = 0;
  637.     tmpl.goAwayFlag = false;
  638.     tmpl.filler2 = 0;
  639.     tmpl.refCon = 0;
  640.     tmpl.itemsID = (**alrt).itemsID;
  641.     tmpl.title[0] = 0;
  642.     return(DlgBeginTemplate(&tmpl));
  643. }
  644.  
  645. /* get a new dialog */
  646. static DialogPtr DlgOpenDLOG(short id)
  647. {
  648.     DialogTemplate tmpl;
  649.     
  650.     ResPtr('DLOG', id, &tmpl, sizeof(DialogTemplate));
  651.     return(DlgBeginTemplate(&tmpl));
  652. }
  653.  
  654. /* This is like GetNewDialog except the same function is used to create
  655.     dialogs and alerts, more error checking is done, and you can't pass
  656.     a pointer to your own storage. The dialog is first searched for in the
  657.     list of preloaded dialogs, and if not found there then a pointer to the
  658.     dialog is allocated. The dialog is always created in an invisible window.
  659.     You must use DlgEnd to dispose of the dialog. The 'type' parameter
  660.     should be either 'DLOG' or 'ALRT'. */
  661. static DialogPtr DlgBeginType(ResType type, short id)
  662. {
  663.     short i = 0;
  664.     volatile DialogPtr dlg = NULL;
  665.     
  666.     require(type == 'DLOG' || type == 'ALRT');
  667.     TRY {
  668.         /* search for dialog in list of preloaded dialogs */
  669.         for (i = 0; i < DLG_PRELOAD_MAX; i++) {
  670.             if (gPreload[i].type == type && id == gPreload[i].id) {
  671.                 if (! gPreload[i].open) {
  672.                     dlg = gPreload[i].dlg;
  673.                     gPreload[i].open = true;
  674.                     /* fix window order, since other windows have been created
  675.                         since this window was displayed */
  676.                     WinLayerSet(dlg, WinLayer(dlg));
  677.                 }
  678.                 break;
  679.             }
  680.         }
  681.         if (! dlg) {
  682.             /* open a new dialog */
  683.             if (type == 'ALRT')
  684.                 dlg = DlgOpenALRT(id);
  685.             else
  686.                 dlg = DlgOpenDLOG(id);
  687.         }
  688.     } CATCH {
  689.         DlgEnd(dlg);
  690.     } ENDTRY;
  691.     ensure(DlgValid(dlg));
  692.     return(dlg);
  693. }
  694.  
  695. /* see description of DlgBeginType */
  696. DialogPtr DlgBeginAlert(short id)
  697. {
  698.     return(DlgBeginType('ALRT', id));
  699. }
  700.  
  701. /* see description of DlgBeginType */
  702. DialogPtr DlgBegin(short id)
  703. {
  704.     return(DlgBeginType('DLOG', id));
  705. }
  706.  
  707. /* Dispose of the dialog; this is analogous to the ToolBox routine
  708.     DisposeDialog. */
  709. void DlgEnd(DialogPtr dlg)
  710. {
  711.     short i = 0;
  712.     
  713.     if (dlg) {
  714.         WinHide(dlg);
  715.         /* search for dialog in list of preloaded dialogs */
  716.         for (i = 0; i < DLG_PRELOAD_MAX; i++) {
  717.             if (dlg == gPreload[i].dlg) {
  718.                 /* dialog was preloaded, so keep it loaded */
  719.                 gPreload[i].open = false;
  720.                 dlg = NULL;
  721.             }
  722.         }
  723.         /* dispose of dialog if it wasn't preloaded */
  724.         if (dlg) {
  725.             DlgUninitialize(dlg);
  726.             DisposeDialog(dlg);
  727.             dlg = NULL;
  728.         }
  729.     }
  730.     ensure(! DlgValid(dlg));
  731. }
  732.  
  733. /*----------------------------------------------------------------------------*/
  734. /* preloading dialogs */
  735. /*----------------------------------------------------------------------------*/
  736.  
  737. /* Create the dialog and add it to the list of preloaded dialogs. If the list
  738.     is full or if the dialog couldn't be created then NULL is returned. */
  739. DialogPtr DlgPreload(ResType type, short id)
  740. {
  741.     volatile DialogPtr dlg = NULL;
  742.     short i = 0;
  743.     
  744.     TRY {
  745.         for (i = 0; i < DLG_PRELOAD_MAX; i++) {
  746.             if (! gPreload[i].dlg) {
  747.                 dlg = DlgBeginType(type, id);
  748.                 gPreload[i].id = id;
  749.                 gPreload[i].type = type;
  750.                 gPreload[i].open = false;
  751.                 gPreload[i].dlg = dlg;
  752.                 break;
  753.             }
  754.         }
  755.     } CATCH {
  756.         DlgEnd(dlg);
  757.     } ENDTRY;
  758. }
  759.  
  760. /* unload a preloaded dialog */
  761. void DlgUnload(ResType type, short id)
  762. {
  763.     short i = 0;
  764.     
  765.     for (i = 0; i < DLG_PRELOAD_MAX; i++) {
  766.         if (gPreload[i].type == type && gPreload[i].id == id) {
  767.             DlgEnd(gPreload[i].dlg);
  768.             memclr(&gPreload[i], sizeof(gPreload[i]));
  769.             break;
  770.         }
  771.     }
  772. }
  773.